package cn.thoughtworks.school.programCenter.services;

import cn.thoughtworks.school.programCenter.entities.*;
import cn.thoughtworks.school.programCenter.exceptions.BusinessException;
import cn.thoughtworks.school.programCenter.feign.DiffCenterFeign;
import cn.thoughtworks.school.programCenter.repositories.AssignmentRepository;
import cn.thoughtworks.school.programCenter.repositories.TaskRepository;
import cn.thoughtworks.school.programCenter.repositories.TopicRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static cn.thoughtworks.school.programCenter.entities.Operation.CREATE;
import static org.apache.commons.lang3.StringUtils.join;

@Service
public class DiffService {
    @Autowired
    private DiffCenterFeign diffCenterFeign;
    @Autowired
    private ProgramService programService;
    @Autowired
    private TopicRepository topicRepository;
    @Autowired
    private TaskRepository taskRepository;
    @Autowired
    private AssignmentRepository assignmentRepository;

    public void updateProgramNotification(Long programId, Long userId, Operation operation) throws BusinessException {
        Program program = programService.findById(programId);
        Map data = buildProgramData(program, userId, operation);
        diffCenterFeign.updateProgramNotification(data);
    }

    public void updateTopicNotification(Long topicId, Long userId, Operation operation) throws BusinessException {
        Topic topic = topicRepository.findById(topicId).orElseThrow(() -> new BusinessException("current topic is not exist"));
        Map data = buildTopicData(topic, userId, operation);
        diffCenterFeign.updateTopicNotification(data);
    }

    public void copyTopicNotification(Topic topic, Long fromId, Long userId, Operation operation) {
        Map data = buildTopicData(topic, userId, operation);
        data.put("fromId", fromId);
        diffCenterFeign.updateTopicNotification(data);
    }

    public void updateTaskNotification(Long taskId, Long userId, Operation operation) throws BusinessException {
        Task task = taskRepository.findById(taskId).orElseThrow(() -> new BusinessException("current task is not exist"));
        Map data = buildTaskData(task, userId, operation);
        data.put("operation", operation);
        diffCenterFeign.updateTaskNotification(data);
    }

    private Map buildTaskData(Task task, Long userId, Operation operation) {
        Map result = new HashMap();
        result.put("topicId", task.getTopicId());
        result.put("taskId", task.getId());
        result.put("title", task.getTitle());
        result.put("content", task.getContent());
        result.put("visible", task.getVisible());
        result.put("operatorId", userId);
        result.put("operation", operation);
        return result;
    }

    public void copyTaskNotification(Task task, Long fromId, Long userId, Operation operation) {
        Map data = buildTaskData(task, userId, operation);
        data.put("fromId", fromId);
        diffCenterFeign.updateTaskNotification(data);
    }

    public void moveTaskNotification(List<Long> ids, Long fromId, Long userId, Operation operation) {
        List<Map> tasks = taskRepository.findByIdIn(ids).stream()
            .map(task -> buildTaskData(task, userId, operation))
            .collect(Collectors.toList());
        tasks.forEach(task -> task.put("fromId", fromId));


        diffCenterFeign.moveTaskNotification(tasks);
    }

    public void updateAssignmentNotification(Long assignmentId, Long userId, Operation operation) throws BusinessException {
        Assignment assignment = assignmentRepository.findById(assignmentId).orElseThrow(() -> new BusinessException("current assignment is not exist"));
        Map data = buildAssignmentData(assignment, userId, operation);
        diffCenterFeign.updateAssignmentsNotification(Arrays.asList(data));
    }

    private Map buildAssignmentData(Assignment assignment, Long userId, Operation operation) {
        List<Long> quizIds = Objects.isNull(assignment.getSelectedQuizzes())
            ? new ArrayList<>()
            : assignment.getSelectedQuizzes().stream()
            .map(AssignmentQuiz::getQuizId).collect(Collectors.toList());
        Map result = new HashMap();
        result.put("taskId", assignment.getTaskId());
        result.put("assignmentId", assignment.getId());
        result.put("title", assignment.getTitle());
        result.put("type", assignment.getType());
        result.put("quizzesId", join(quizIds, ","));
        result.put("visible", assignment.getVisible());
        result.put("operatorId", userId);
        result.put("operation", operation);
        return result;
    }

    private Map buildTopicData(Topic topic, Long userId, Operation operation) {
        Map result = new HashMap();
        result.put("topicId", topic.getId());
        result.put("title", topic.getTitle());
        result.put("visible", topic.getVisible());
        result.put("programId", topic.getProgramId());
        result.put("operatorId", userId);
        result.put("operation", operation);
        return result;
    }


    private Map buildProgramData(Program program, Long userId, Operation operation) {
        Map result = new HashMap();
        result.put("programId", program.getId());
        result.put("title", program.getTitle());
        result.put("startTime", program.getStartTime());
        result.put("endTime", program.getEndTime());
        result.put("category", program.getCategory());
        result.put("introduction", program.getIntroduction());
        result.put("studentLink", program.isStudentLink());
        result.put("tutorLink", program.isTutorLink());
        result.put("isReadOnly", program.isReadOnly());
        result.put("isExpired", program.isExpired());
        result.put("available", program.getAvailable());
        result.put("organizationId", program.getOrganizationId());
        result.put("operatorId", userId);
        result.put("operation", operation);
        return result;
    }

    public void copyProgramNotification(Program program, Long fromId, Long userId, Operation operation) {
        Map data = buildProgramData(program, userId, operation);
        data.put("operation", operation);
        data.put("fromId", fromId);
        diffCenterFeign.updateProgramNotification(data);
    }

    public void copyAssignmentsNotification(List<Assignment> assignments, Long userId, Operation operation) {
        List<Map> data = assignments.stream()
            .map(assignment -> buildAssignmentData(assignment, userId, operation))
            .collect(Collectors.toList());
        diffCenterFeign.updateAssignmentsNotification(data);
    }

    public void initData() {
        initPrograms();
        initTopics();
        initTasks();
        initAssignments();
    }

    private void initAssignments() {
        List<Map> data = assignmentRepository.findAll().stream()
            .map(assignment -> buildAssignmentData(assignment, assignment.getCreatorId(), CREATE))
            .collect(Collectors.toList());
        diffCenterFeign.updateAssignmentsNotification(data);

    }

    private void initTasks() {
        Long DEFAULT_CREATOR_ID = 0L;
        List<Map> data = taskRepository.findAll().stream()
            .map(task -> buildTaskData(task, DEFAULT_CREATOR_ID, CREATE))
            .collect(Collectors.toList());
        diffCenterFeign.moveTaskNotification(data);
    }

    private void initTopics() {
        Long DEFAULT_CREATOR_ID = 0L;

        topicRepository.findAll().stream()
            .map(topic -> buildTopicData(topic, DEFAULT_CREATOR_ID, CREATE))
            .forEach(topic -> diffCenterFeign.updateTopicNotification(topic));
    }

    private void initPrograms() {
        programService.findAll().stream()
            .map(program -> buildProgramData(program, program.getCreatorId(), CREATE))
            .forEach(program -> diffCenterFeign.updateProgramNotification(program));
    }
}
